package cn.topevery.garden.dal.logic;

import cn.hutool.core.collection.CollectionUtil;
import cn.topevery.garden.dal.logic.event.EventInfoLogic;
import cn.topevery.garden.dal.util.CacheClient;
import cn.topevery.garden.dal.util.GeomHelper;
import cn.topevery.garden.entity.po.BaseInfo;
import cn.topevery.garden.entity.vo.FacilitiesClassVO;
import hw.topevery.basis.dal.logic.SysDictLogic;
import hw.topevery.basis.entity.fetch.DictFetch;
import hw.topevery.basis.entity.po.SysDict;
import hw.topevery.framework.GeometryHelper;
import hw.topevery.framework.dto.PointDto;
import hw.topevery.framework.enums.GeometryTypeEnum;
import hw.topevery.framework.web.JsonResult;
import org.locationtech.jts.geom.Geometry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.Year;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class OneMapLogic {
    @Autowired
    private BaseInfoLogic baseInfoLogic;

    @Autowired
    private FacilitiesLogic facilitiesLogic;

    @Autowired
    private FacilitiesClassLogic facilitiesClassLogic;

    @Autowired
    private GreLandLogic greLandLogic;

    @Autowired
    private EventInfoLogic eventInfoLogic;

    @Autowired
    private CacheClient cacheClient;

    @Autowired
    private PlanLogic planLogic;

    @Autowired
    private SysDictLogic sysDictLogic;

    private static final String OVERVIEW_KEY = "overview:";
    private static final String OVERVIEW_LOCK = "overview:lock:";

    private static final Long LOCK_TTL = 2L;
    private static final Long CACHE_TTL = 30L;
    private static final Long CACHE_NULL_TTL = 1L;

    public Map<String, Object> gardenOverview() {
/*        Map resultMap = cacheClient.queryWithLogicalExpire(OVERVIEW_KEY, baseInfoLogic, Map.class, (baseInfoLogic) -> {
            BaseInfo baseinfo = baseInfoLogic.getEntityByColumn("c_map_id", "f41889ad-585b-413d-878e-7040991f468d");
            Map<String, Object> map = new HashMap<String, Object>() {{
                put("greenCoverage", baseinfo.getGreenCoverage());
                put("greeningRate", baseinfo.getGreeningRate());
                put("parkPerCapital", baseinfo.getParkPerCapital());
            }};
            return map;
        }, OVERVIEW_LOCK, LOCK_TTL, CACHE_TTL, CACHE_NULL_TTL, TimeUnit.MINUTES);
        return resultMap;*/
        return null;
    }

    private static final String FACILITIES_COUNT_KEY = "facilities:count:";
    private static final String FACILITIES_COUNT_LOCK = "facilities:lock:";

    public List<Map<String, Object>> facilitiesCount() {
/*        List list = cacheClient.queryListWithMutex(FACILITIES_COUNT_KEY, this, Map.class,
                (Function<OneMapLogic, List>) OneMapLogic::getFacilitiesCount,
                FACILITIES_COUNT_LOCK, LOCK_TTL, CACHE_TTL, CACHE_NULL_TTL, TimeUnit.MINUTES);
        return list;*/
        return null;
    }

    private List<Map<String, Object>> getFacilitiesCount() {
        CompletableFuture<List<Map<String, Object>>> facilitiesFuture = CompletableFuture.supplyAsync(() -> facilitiesLogic.facilitiesCount());
        //公园和绿地的数据补充
        CompletableFuture<List<Map<String, Object>>> greFuture = CompletableFuture.supplyAsync(() -> greLandLogic.getLandAndParkCount());
        CompletableFuture<List<FacilitiesClassVO>> facilitiesClassFuture = CompletableFuture.supplyAsync(() -> facilitiesClassLogic.getTree());
        List<Map<String, Object>> mapList = null;
        List<Map<String, Object>> greList = null;
        List<FacilitiesClassVO> classTree = null;
        try {
            mapList = facilitiesFuture.get();
            greList = greFuture.get();
            classTree = facilitiesClassFuture.get();
            if (CollectionUtil.isNotEmpty(classTree)) {
                List<FacilitiesClassVO> children1 = classTree.get(0).getChildren();
                if (CollectionUtil.isNotEmpty(children1)) {
                    List<String> typeList = mapList.stream().map(m -> m.getOrDefault("typeId", "").toString()).collect(Collectors.toList());
                    for (FacilitiesClassVO c : children1) {
                        if (!typeList.contains(c.getEntityId())) {
                            mapList.add(new HashMap<String, Object>(3) {{
                                put("typeName", c.getName());
                                put("count", 0);
                                put("typeId", c.getEntityId());
                            }});
                        }
                    }
                }
            }
            mapList.addAll(greList);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            return mapList;
        }
    }

    public List<Map<String, Object>> eventCount(Integer countType) {
        List<Map<String, Object>> resultList = null;
        DateTimeFormatter formatter = null;
        LocalDate date = LocalDate.now();
        List<String> days = new ArrayList<>();
        switch (countType) {
            case 1:
                resultList = eventInfoLogic.eventCount("'%Y-%m-%d'", "6 DAY");
                formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
                for (int i = 6; i >= 0; i--) {
                    LocalDate localDate = date.minusDays(i);
                    days.add(localDate.format(formatter));
                }
                resultList = addNullDate(resultList, days, formatter, countType);
                break;
            case 2:
                resultList = eventInfoLogic.eventCount("'%Y-%m'", "1 YEAR");
                formatter = DateTimeFormatter.ofPattern("yyyy-MM");
                for (int i = 11; i >= 0; i--) {
                    LocalDate localDate = date.minusMonths(i);
                    days.add(localDate.format(formatter));
                }
                resultList = addNullDate(resultList, days, formatter, countType);
                break;
            case 3:
                resultList = eventInfoLogic.eventCount("'%Y'", "2 YEAR");
                formatter = DateTimeFormatter.ofPattern("yyyy");
                for (int i = 2; i >= 0; i--) {
                    LocalDate localDate = date.minusYears(i);
                    days.add(localDate.format(formatter));
                }
                resultList = addNullDate(resultList, days, formatter, countType);
                break;
            default:
                break;
        }
        return resultList;
    }

    private List<Map<String, Object>> addNullDate(List<Map<String, Object>> resultList, List<String> days, DateTimeFormatter formatter, Integer countType) {
        List<String> dateHaveData = resultList.stream().map(r -> r.get("date").toString()).collect(Collectors.toList());
        for (String day : days) {
            if (!dateHaveData.contains(day)) {
                resultList.add(new HashMap<String, Object>(4) {{
                    put("date", day);
                    put("registerNum", 0);
                    put("disposeNum", 0);
                    put("completeNum", 0);
                }});
            }
        }
        switch (countType) {
            case 1:
                return resultList.stream().sorted(
                        Comparator.comparing(m -> LocalDate.parse(m.get("date").toString(), formatter))
                ).collect(Collectors.toList());
            case 2:
                return resultList.stream().sorted(
                        Comparator.comparing(m -> YearMonth.parse(m.get("date").toString(), formatter))
                ).collect(Collectors.toList());
            case 3:
                return resultList.stream().sorted(
                        Comparator.comparing(m -> Year.parse(m.get("date").toString(), formatter))
                ).collect(Collectors.toList());
            default:
                return resultList;
        }
    }

    private static final String PLAN_COUNT_KEY = "plan:count:";
    private static final String PLAN_COUNT_LOCK = "plan:lock:";

    public List<Map<String, Object>> planCount() {
/*        List list = cacheClient.queryListWithMutex(PLAN_COUNT_KEY, this, Map.class, (Function<OneMapLogic, List>) OneMapLogic::getPlanCount,
                PLAN_COUNT_LOCK, LOCK_TTL, CACHE_TTL, CACHE_NULL_TTL, TimeUnit.MINUTES);
        return list;*/
        return null;
    }

    private List<Map<String, Object>> getPlanCount() {
        List<Map<String, Object>> result = planLogic.planCount();
        DictFetch dictFetch = new DictFetch();
        dictFetch.setNsId("8240d40c-65cf-4b61-96cb-6b1796338e32");
        List<SysDict> planTypes = sysDictLogic.getListDict(dictFetch);
        List<String> types = result.stream().map(m -> m.getOrDefault("maintainType", "").toString()).collect(Collectors.toList());
        planTypes.forEach(planType -> {
            if (!types.contains(planType.dictId)) {
                result.add(new HashMap<String, Object>(5) {{
                    put("maintainType", planType.dictId);
                    put("maintainTypeName", planType.dictName);
                    put("planCount", 0);
                    put("completeCount", 0);
                    put("completeRate", 0);
                }});
            }
        });
        return result;
    }

    public JsonResult<?> parkPositionSelect(Map<String, List<List<PointDto>>> param) {
        List<List<PointDto>> mapRangesPoints = param.get("mapRangesPoints");
        if (mapRangesPoints == null) {
            return JsonResult.error("请求坐标参数不存在！");
        }
        //获取坐标外扩200m后的任意一点(实际为图形边角中的某一点)
        Geometry orgMkt = GeometryHelper.buildMercatorGeometry(mapRangesPoints, GeometryTypeEnum.PLANE);
        Geometry bufferMkt = greLandLogic.getGeoBuffer(GeometryHelper.getGeomWellKnownText(orgMkt), 400D);
        //查询所有公园服务半径
        List<Geometry> parkRadiusMkts = greLandLogic.getAllGeoRadius();
//        List<List<PointDto>> mktPointList = GeometryHelper.getMercatorGeomePoint(bufferMkt);//getMercatorGeomePoint()方法会直接将坐标转为wgs84
        List<List<PointDto>> mktPointList = GeometryHelper.convertGeometryToPoint(bufferMkt);
        for (PointDto pointDto : mktPointList.get(0)) {
            List<Boolean> result = new ArrayList<>(parkRadiusMkts.size());
            //判断此园与各公园服务半径范围是否有重合，若没有则返回
            for (Geometry parkRadiusMkt : parkRadiusMkts) {
                boolean isNear = GeomHelper.inGeom(parkRadiusMkt, pointDto.getLng(), pointDto.getLat(), 200);
                result.add(isNear);
            }
            if (result.contains(true)) {
                //此点不合适，测试下一点
                continue;
            }
            String mktPointWkt = "POINT(" + pointDto.getLng() + " " + pointDto.getLat() + ")";
            //获取半径200m范围的圆
            Geometry targetCircleMkt = greLandLogic.getGeoBuffer(mktPointWkt, 200D);
            //还需判断园与请求数据mapRangesPoints的区域是否有重合
            double geomDistance = GeomHelper.getGeomDistance(orgMkt, targetCircleMkt);
            if (geomDistance >= 0) {
                List<List<PointDto>> wgsPoint = GeometryHelper.getMercatorGeomePoint(targetCircleMkt);
                return JsonResult.ok(wgsPoint);
            }
        }
        return JsonResult.error("未找到适合的规划范围");
    }
}